//
// Created by none98 on 2021/9/10.
//
#include <memory>
#include "TcpConnection.h"
#include "Selector.h"
#include <unistd.h>
#include <spdlog/spdlog.h>
#include <netinet/in.h>
#include "Channel.h"
#include "EventLoop.h"
#include "Timer.h"

TcpConnection::TcpConnection(int fd, EventLoop* loop):
    fd(fd),
    channel(new Channel(fd,Selector::READ)),
    inputBuffer(),
    outputBuffer(),
    loop(loop),
    context(nullptr)
{
    //set local/remote address;
    struct sockaddr_in peerAddr;
    size_t len=sizeof(peerAddr);
    if(getsockname(fd, reinterpret_cast<sockaddr *>(&peerAddr), reinterpret_cast<socklen_t *>(&len)) < 0){
        spdlog::error("socket:{} get peer address go wrong! {}",fd,strerror(errno));
    }else{
        remoteAddress = InetAddress(static_cast<int>(peerAddr.sin_port),reinterpret_cast<char*>(&peerAddr.sin_addr));
    }
    channel->SetReadCallBack(std::bind(&TcpConnection::HandleRead,this));
    channel->SetErrorCallBack(std::bind(&TcpConnection::HandleError,this));
    channel->SetWriteCallBack(std::bind(&TcpConnection::HandleWrite,this));
}



void TcpConnection::HandleRead() {
    const int bufferLen=65536;
    char buffer[bufferLen];
    int length=0;
    if( (length = read(fd,&buffer,bufferLen)) ==0){
        //close fd
        //handle close
        if(closeCallBack){
            closeCallBack(shared_from_this());
        }
        if(closeInTcpServer){
            loop->AddTask(std::bind(closeInTcpServer,shared_from_this()));
        }else{
            spdlog::error("no close for server");
        }
    }else if(length>0){
        //put buffer into inputBuffer
        inputBuffer.Write(buffer,length);
        if(msgCallBack){
            msgCallBack(shared_from_this(),&inputBuffer);
        }
    }else {
        if(errno==EAGAIN || errno == EINTR)
            spdlog::error("Socket:{} is in error! {}", this->fd, strerror(errno));
        else{
//            spdlog::error("close unnormal!!!!");
            if(closeCallBack){
                closeCallBack(shared_from_this());
            }
            if(closeInTcpServer){
                loop->AddTask(std::bind(closeInTcpServer,shared_from_this()));
            }else{
                spdlog::error("no close for server");
            }
        }
    }
}

void TcpConnection::HandleWrite() {
    //write outputBuffer into socket buffer, if the outputBuffer is empty, then close the write event
    std::unique_lock<std::mutex> lock(outMutex);
    size_t size = outputBuffer.ReadableSize();
    int wsize = write(fd,&(*std::next(outputBuffer.data.begin(),outputBuffer.readIndex)),size);
    outputBuffer.readIndex+=wsize;
    if(outputBuffer.IsEmpty()) {
        outputBuffer.readIndex=0;
        outputBuffer.writeIndex=0;
//        if(outputBuffer.data.size()>64*1024) {
//            outputBuffer.data.resize(8*1024);
//            outputBuffer.data.shrink_to_fit();
//        }
        channel->CancelEvent(Selector::WRITE);
        loop->GetSelector()->UpdateChannel(channel.get());
        if(writeCompleteCallBack){
            writeCompleteCallBack(shared_from_this());
        }
    }
}

void TcpConnection::HandleError() {

}

void TcpConnection::Send(const std::string &msg) {
    std::unique_lock<std::mutex> lock(outMutex);
    outputBuffer.Write(const_cast<char *>(&(*msg.begin())), msg.size());
    if( (channel->GetInterestedEvents()&Selector::WRITE) ==0){
        channel->SetEvent(Selector::WRITE);
        loop->GetSelector()->UpdateChannel(channel.get());
    }
}

void TcpConnection::SetEventLoop(EventLoop *loop) {
    this->loop=loop;
    loop->GetSelector()->RegisterChannel(channel.get());
}


TcpConnection::~TcpConnection() {
    close(fd);
}



long TcpConnection::AddTimer(int hours,int minutes,int seconds,int milliSeconds,const TimerCallBack &callBack,int isCycle){
    return loop->AddTimer(Timer(TimeStamp(hours,minutes,seconds,milliSeconds),callBack,isCycle,fd));
}

void TcpConnection::CancelTimer(long seq) {
    loop->CancelTimerBySeq(seq);
}

void TcpConnection::Close() {
    if(closeCallBack){
        closeCallBack(shared_from_this());
    }
    loop->AddTask(std::bind(closeInTcpServer,shared_from_this()));
}
